
/* Copyright (c) Mark J. Kilgard, 1994. */

/* This program is freely distributable without licensing fees
   and is provided without guarantee or warrantee expressed or
   implied. This program is -not- in the public domain. */

#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include <GL/glut.h>

enum {
	BRASS, RED_PLASTIC, EMERALD, SLATE
} MaterialType;
enum {
	TORUS_MATERIAL = 1, TEAPOT_MATERIAL = 2, ICO_MATERIAL = 3
} MaterialDisplayList;
enum {
	LIGHT_OFF, LIGHT_RED, LIGHT_WHITE, LIGHT_GREEN
} LightValues;

GLfloat red_light[] =
{1.0, 0.0, 0.0, 1.0}, green_light[] =
{0.0, 1.0, 0.0, 1.0}, white_light[] =
{1.0, 1.0, 1.0, 1.0};
GLfloat left_light_position[] =
{-1.0, 0.0, 1.0, 0.0}, right_light_position[] =
{1.0, 0.0, 1.0, 0.0};
GLfloat brass_ambient[] =
{0.33, 0.22, 0.03, 1.0}, brass_diffuse[] =
{0.78, 0.57, 0.11, 1.0}, brass_specular[] =
{0.99, 0.91, 0.81, 1.0}, brass_shininess = 27.8;
GLfloat red_plastic_ambient[] =
{0.0, 0.0, 0.0}, red_plastic_diffuse[] =
{0.5, 0.0, 0.0}, red_plastic_specular[] =
{0.7, 0.6, 0.6}, red_plastic_shininess = 32.0;
GLfloat emerald_ambient[] =
{0.0215, 0.1745, 0.0215}, emerald_diffuse[] =
{0.07568, 0.61424, 0.07568}, emerald_specular[] =
{0.633, 0.727811, 0.633}, emerald_shininess = 76.8;
GLfloat slate_ambient[] =
{0.02, 0.02, 0.02}, slate_diffuse[] =
{0.02, 0.01, 0.01}, slate_specular[] =
{0.4, 0.4, 0.4}, slate_shininess = .78125;
int shade_model = GL_SMOOTH;
char *left_light, *right_light;
char *ico_material, *teapot_material, *torus_material;

void
output(GLfloat x, GLfloat y, char *format,...)
{
	va_list args;
	char buffer[200], *p;

	va_start(args, format);
	vsprintf(buffer, format, args);
	va_end(args);
	glPushMatrix();
	glTranslatef(x, y, 0);
	for (p = buffer; *p; p++)
		glutStrokeCharacter(GLUT_STROKE_ROMAN, *p);
	glPopMatrix();
}

void
display(void)
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glScalef(1.3, 1.3, 1.3);
	glRotatef(20.0, 1.0, 0.0, 0.0);
	glPushMatrix();
	glTranslatef(-0.65, 0.7, 0.0);
	glRotatef(90.0, 1.0, 0.0, 0.0);
	glCallList(TORUS_MATERIAL);
	glutSolidTorus(0.275, 0.85, 10, 15);
	glPopMatrix();
	glPushMatrix();
	glTranslatef(-0.75, -0.8, 0.0);
	glCallList(TEAPOT_MATERIAL);
	glutSolidTeapot(0.7);
	glPopMatrix();
	glPushMatrix();
	glTranslatef(1.0, 0.0, -1.0);
	glCallList(ICO_MATERIAL);
	glutSolidIcosahedron();
	glPopMatrix();
	glPopMatrix();
	glPushAttrib(GL_ENABLE_BIT);
	glDisable(GL_DEPTH_TEST);
	glDisable(GL_LIGHTING);
	glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	glLoadIdentity();
	gluOrtho2D(0, 3000, 0, 3000);
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glLoadIdentity();
	output(80, 2800, "Torus: %s", torus_material);
	output(80, 2650, "Icosahedron: %s", ico_material);
	output(80, 2500, "Teapot: %s", teapot_material);
	output(80, 250, "Left light: %s", left_light);
	output(1700, 250, "Right light: %s", right_light);
	output(850, 100, "Shade model: %s",
	       shade_model == GL_SMOOTH ? "smooth" : "flat");
	glPopMatrix();
	glMatrixMode(GL_PROJECTION);
	glPopMatrix();
	glPopAttrib();
	glutSwapBuffers();
}

void
light_select(GLenum which, int value, char **label)
{
	glEnable(which);
	switch (value) {
	case LIGHT_OFF:
		*label = "off";
		glDisable(which);
		break;
	case LIGHT_RED:
		*label = "red";
		glLightfv(which, GL_DIFFUSE, red_light);
		break;
	case LIGHT_WHITE:
		*label = "white";
		glLightfv(which, GL_DIFFUSE, white_light);
		break;
	case LIGHT_GREEN:
		*label = "green";
		glLightfv(which, GL_DIFFUSE, green_light);
		break;
	}
	glutPostRedisplay();
}

void
left_light_select(int value)
{
	light_select(GL_LIGHT0, value, &left_light);
}

void
right_light_select(int value)
{
	light_select(GL_LIGHT1, value, &right_light);
}

void
material(int dlist, GLfloat * ambient, GLfloat * diffuse,
         GLfloat * specular, GLfloat shininess)
{
	glNewList(dlist, GL_COMPILE);
	glMaterialfv(GL_FRONT, GL_AMBIENT, ambient);
	glMaterialfv(GL_FRONT, GL_DIFFUSE, diffuse);
	glMaterialfv(GL_FRONT, GL_SPECULAR, specular);
	glMaterialf(GL_FRONT, GL_SHININESS, shininess);
	glEndList();
}

char *
material_select(int object, int value)
{
	glutPostRedisplay();
	switch (value) {
	case BRASS:
		material(object, brass_ambient,
		         brass_diffuse, brass_specular, brass_shininess);
		return "brass";
	case RED_PLASTIC:
		material(object, red_plastic_ambient, red_plastic_diffuse,
		         red_plastic_specular, red_plastic_shininess);
		return "red plastic";
	case EMERALD:
		material(object, emerald_ambient, emerald_diffuse,
		         emerald_specular, emerald_shininess);
		return "emerald";
	case SLATE:
		material(object, slate_ambient, slate_diffuse,
		         slate_specular, slate_shininess);
		return "slate";
	}
	return NULL; /* avoid bogus warning! */
}

void
torus_select(int value)
{
	torus_material = material_select(TORUS_MATERIAL, value);
}

void
teapot_select(int value)
{
	teapot_material = material_select(TEAPOT_MATERIAL, value);
}

void
ico_select(int value)
{
	ico_material = material_select(ICO_MATERIAL, value);
}

void
main_menu_select(int value)
{
	if (value == 666)
		exit(0);
	glShadeModel(shade_model = value);
	glutPostRedisplay();
}

int
main(int argc, char **argv)
{
	int left_light_m, right_light_m, torus_m, teapot_m, ico_m;

	glutInitWindowSize(400, 400);
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
	glutCreateWindow("Lighting Laboratory");
	glutDisplayFunc(display);

#define LIGHT_MENU_ENTRIES() \
    glutAddMenuEntry("Disable", LIGHT_OFF); \
    glutAddMenuEntry("Red", LIGHT_RED); \
    glutAddMenuEntry("White", LIGHT_WHITE); \
    glutAddMenuEntry("Green", LIGHT_GREEN);
#define MATERIAL_MENU_ENTRIES() \
    glutAddMenuEntry("Brass", BRASS); \
    glutAddMenuEntry("Red plastic", RED_PLASTIC); \
    glutAddMenuEntry("Emerald", EMERALD); \
    glutAddMenuEntry("Slate", SLATE);

	left_light_m = glutCreateMenu(left_light_select);
	LIGHT_MENU_ENTRIES();
	right_light_m = glutCreateMenu(right_light_select);
	LIGHT_MENU_ENTRIES();
	torus_m = glutCreateMenu(torus_select);
	MATERIAL_MENU_ENTRIES();
	teapot_m = glutCreateMenu(teapot_select);
	MATERIAL_MENU_ENTRIES();
	ico_m = glutCreateMenu(ico_select);
	MATERIAL_MENU_ENTRIES();

	glutCreateMenu(main_menu_select);
	glutAddMenuEntry("Smooth shading", GL_SMOOTH);
	glutAddMenuEntry("Flat shading", GL_FLAT);
	glutAddSubMenu("Left light", left_light_m);
	glutAddSubMenu("Right light", right_light_m);
	glutAddSubMenu("Torus", torus_m);
	glutAddSubMenu("Teapot", teapot_m);
	glutAddSubMenu("Icosahedron", ico_m);
	glutAddMenuEntry("Quit", 666);
	glutAttachMenu(GLUT_RIGHT_BUTTON);

	glLightfv(GL_LIGHT0, GL_POSITION, left_light_position);
	glLightfv(GL_LIGHT0, GL_SPECULAR, white_light);
	glLightfv(GL_LIGHT1, GL_POSITION, right_light_position);
	glLightfv(GL_LIGHT1, GL_SPECULAR, white_light);
	left_light_select(LIGHT_RED);
	right_light_select(LIGHT_GREEN);
	torus_select(RED_PLASTIC);
	teapot_select(BRASS);
	ico_select(EMERALD);
	glEnable(GL_LIGHTING);
	glEnable(GL_DEPTH_TEST);
	glEnable(GL_NORMALIZE);
	glLineWidth(1.0);
	glMatrixMode(GL_PROJECTION);
	gluPerspective( /* degrees field of view */ 50.0,
	        /* aspect ratio */ 1.0, /* Z near */ 1.0, /* Z far */ 10.0);
	glMatrixMode(GL_MODELVIEW);
	gluLookAt(0.0, 0.0, 5.0,  /* eye is at (0,0,5) */
	          0.0, 0.0, 0.0,      /* center is at (0,0,0) */
	          0.0, 1.0, 0.);      /* up is in positive Y direction */
	glTranslatef(0.0, 0.0, -1.0);

	glutMainLoop();
	return 0;             /* ANSI C requires main to return int. */
}
